package com.develop.data.loan.manager.change.amount;

import com.develop.data.loan.manager.change.amount.api.LoanInstallmentAmountBudget;
import com.develop.data.loan.model.LoanContract;
import com.develop.data.loan.model.LoanInstallment;
import com.develop.data.loan.option.LoanRateType;
import org.javamoney.moneta.Money;
import org.springframework.stereotype.Component;

import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.MonetaryRounding;
import javax.money.RoundingQueryBuilder;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;

@Component("L0001B0001LoanInstallmentInterestBudget")
public class L0001B0001LoanInstallmentInterestBudget implements LoanInstallmentAmountBudget {

    /* 已发生形态转移 */
    /* 持有时间，左受制于动帐时间，右受制于转非应计时间（不计息） */
    /* LoanInstallmentInterestCleared对应Installment的InterestCleared金额字段 */

    public MonetaryAmount preview(LoanInstallment installment, LocalDateTime localDate){

        /* 第M期为当前期，预览第N期 */
        MonetaryAmount amount = Money.zero(Monetary.getCurrency("CNY"));
        LocalDateTime cursorDate = Optional.ofNullable(installment.getInstallmentCursor()).orElse(LocalDateTime.MIN);
        LocalDateTime freezeDate = Optional.ofNullable(installment.getInstantFrozen()).orElse(LocalDateTime.MAX);

        LoanContract contract = installment.getLoanContract();
        LoanRateType rateType = contract.getLoanRateType();
        BigDecimal rateDaily = new BigDecimal(contract.getContractRateValue());
        
        LocalDateTime valueDate =installment.getInstallmentHead();
        LocalDateTime headDate = installment.getInstallmentHead();
        LocalDateTime tailDate = installment.getInstallmentTail();


        /* 历史期次，每期首尾左闭右开，每期天数算头不算尾 */
        if (Duration.between(tailDate, localDate).toDays() >= 0) {

            /* 用于计算时长，左受限于起息日，右受限于应计时间 */
            LocalDateTime leftDate = cursorDate.isAfter(tailDate)?cursorDate: tailDate;
            LocalDateTime rightDate = localDate.isBefore(freezeDate)? localDate : freezeDate;
            long days = Duration.between(leftDate, rightDate).toDays() + 1;
            MonetaryAmount principal = installment.getInstallmentPrincipalDueAccrual(); /* 逾期以后，为零，相当于空跑 */
            MonetaryRounding rounding = Monetary.getRounding(RoundingQueryBuilder.of().setScale(2).set(RoundingMode.FLOOR).build());
            amount = principal.multiply(days).multiply(rateDaily).with(rounding);
            amount = amount.add(installment.getInstallmentInterestDueAccrual());
            return amount;
        }

        /* 未来期次，每期首尾左闭右开，每期天数算头不算尾 */
        if (Duration.between(localDate, headDate).toDays() > 0) {
            amount = Money.zero(Monetary.getCurrency("CNY"));
            return amount;
        }

        /* 当前期次，排除历史期次，排除未来期次 */
        if (Duration.between(localDate, tailDate).toDays() > 0 && Duration.between(headDate, localDate).toDays() >= 0) {
            MonetaryAmount principal = installment.getContractPrincipalAccrualOpening().subtract(
                    installment.getInstallmentPrincipalCleared()
            );

            /*累计的*/
            amount = amount.add(installment.getInstallmentInterestAccrual());
            /*新增的*/
            if(Duration.between(cursorDate, localDate).toDays() > 0){
                LocalDateTime leftDate = cursorDate.isBefore(valueDate) ? valueDate : cursorDate;
                LocalDateTime rightDate = localDate.isBefore(freezeDate) ? localDate : freezeDate;
                long days = Duration.between(leftDate, rightDate).toDays() + 1;
                MonetaryRounding rounding = Monetary.getRounding(RoundingQueryBuilder.of().setScale(2).set(RoundingMode.FLOOR).build());
                amount = amount.add(
                    principal.multiply(days).multiply(rateDaily).with(rounding)
                );
            }
            /*已还的*/
            amount = amount.subtract(installment.getInstallmentInterestCleared());
        }

        return amount;
        
    }

    public MonetaryAmount execute(LoanInstallment loanInstallment, LocalDateTime localDate, MonetaryAmount amount){

        /* 有借必有贷 */
        /* 还款金额任意，按金额依次扣除，直到扣完 */
        /* 第M期为当前期，动帐第N期 */

        MonetaryAmount amountPreviewed = this.preview(loanInstallment, localDate);

        LocalDateTime cursorDate = Optional.ofNullable(loanInstallment.getInstallmentCursor()).orElse(LocalDateTime.MIN);
        LocalDateTime freezeDate = Optional.ofNullable(loanInstallment.getInstantFrozen()).orElse(LocalDateTime.MAX);
        LocalDateTime valueDate = loanInstallment.getInstallmentHead();

        /* 当前期次、历史期次 */
        if(Duration.between(valueDate, cursorDate).toDays() >= 0){

            /* 新的动帐日 */
            if(Duration.between(cursorDate, localDate).toDays() > 0){

                LoanContract contract = loanInstallment.getLoanContract();
                LoanRateType rateType = contract.getLoanRateType();
                BigDecimal rateDaily = new BigDecimal(contract.getContractRateValue());

                MonetaryAmount principal = loanInstallment.getContractPrincipalAccrualOpening().subtract(
                        loanInstallment.getInstallmentPrincipalCleared()
                );
                LocalDateTime leftDate = cursorDate.isBefore(valueDate) ? valueDate : cursorDate;
                LocalDateTime rightDate = localDate.isBefore(freezeDate) ? localDate : freezeDate;
                long days = Duration.between(leftDate, rightDate).toDays() + 1;
                MonetaryRounding rounding = Monetary.getRounding(RoundingQueryBuilder.of().setScale(2).set(RoundingMode.FLOOR).build());

                loanInstallment.setInstallmentInterestAccrual(
                        loanInstallment.getInstallmentInterestAccrual().add(
                                principal.multiply(days).multiply(rateDaily).with(rounding)
                        )
                );
            }
        }

        loanInstallment.setInstallmentInterestAccrual(amountPreviewed);
        if(amount.isGreaterThanOrEqualTo(amountPreviewed)){
            loanInstallment.setInstallmentInterestCleared(loanInstallment.getInstallmentInterestCleared().add(amountPreviewed));
            loanInstallment.setInstallmentCursor(localDate);
            return amount.subtract(amountPreviewed);
        };
        loanInstallment.setInstallmentInterestCleared(loanInstallment.getInstallmentInterestCleared().add(amount));
        loanInstallment.setInstallmentCursor(localDate);
        return Money.zero(Monetary.getCurrency("CNY"));

    }
}
